import { getRequestIP } from "h3"; import { getPublicPostCommentContext } from "#server/service/posts"; import { createComment } from "#server/service/post-comments"; import { notifyReplyCommentCreated } from "#server/service/comment-notify"; import { assertUnderRateLimit } from "#server/utils/simple-rate-limit"; import { parsePublicPostCreateCommentBody } from "#server/api/public/comments-create-body"; export default defineEventHandler(async (event) => { const publicSlug = event.context.params?.publicSlug; const postSlug = event.context.params?.postSlug; if (!publicSlug || !postSlug || typeof publicSlug !== "string" || typeof postSlug !== "string") { throw createError({ statusCode: 400, statusMessage: "无效请求" }); } const ctx = await getPublicPostCommentContext(publicSlug, postSlug); if (!ctx) { throw createError({ statusCode: 404, statusMessage: "未找到" }); } const viewer = await event.context.auth.getCurrent(); const ip = getRequestIP(event, { xForwardedFor: true }) ?? "unknown"; if (viewer == null) { assertUnderRateLimit(`comment-post-guest:${ip}`, 20, 15 * 60 * 1000); } else { assertUnderRateLimit(`comment-post-user:${viewer.id}`, 60, 15 * 60 * 1000); } const body = await readBody<{ parentId?: number | null; guestDisplayName?: string; guestEmail?: string; guestIsAnonymous?: boolean; body: unknown; }>(event); const parsed = parsePublicPostCreateCommentBody(body); const newCommentId = await createComment({ postId: ctx.id, ownerUserId: ctx.userId, parentId: parsed.parentId, viewer, guestDisplayName: parsed.guestDisplayName, guestEmail: parsed.guestEmail, guestIsAnonymous: parsed.guestIsAnonymous, body: parsed.body, }); void notifyReplyCommentCreated({ postId: ctx.id, commentId: newCommentId, parentId: parsed.parentId, actorUserId: viewer?.id ?? null, replyBody: parsed.body, }); return R.success({ id: newCommentId }); });